﻿using System;
using System.Diagnostics.Contracts;
using System.Reflection;

namespace Postnuclear
  {
  /// <summary>
  /// Extension methods for <see cref="MemberInfo"/>, <see cref="MethodInfo"/>.
  /// </summary>  
  public static partial class ExtensionMethods
    {
    #region Public

    // Methods

    /// <summary>
    /// Gets the type of the member.
    /// </summary>
    /// <param name="member">The member.</param>
    /// <returns>Type of the member.</returns>
    [Pure]
    public static Type GetMemberType(this MemberInfo member)
      {
      Contract.Requires<ArgumentNullException>(member != null);

      switch (member.MemberType)
        {
        case MemberTypes.Field:
          return ((FieldInfo)member).FieldType;

        case MemberTypes.Property:
          return ((PropertyInfo)member).PropertyType;

        default:
          Contract.Assert(false, "Normally we can't get here.");
          throw new InvalidOperationException();
        }
      }

    /// <summary>
    /// Gets method information and returns the same method with specified generic arguments.
    /// </summary>
    /// <param name="method">The method information.</param>
    /// <param name="typeArguments">The type arguments.</param>
    /// <returns>Method with specified generic arguments.</returns>
    public static MethodInfo ToGeneric(this MethodInfo method, params Type[] typeArguments)
      {
      Contract.Requires<ArgumentNullException>(method != null);
      Contract.Requires<ArgumentNullException>(typeArguments != null);
      Contract.Requires<ArgumentNullException>(method.GetGenericArguments().Length == typeArguments.Length);

      return (method.IsGenericMethodDefinition ? method : method.GetGenericMethodDefinition()).MakeGenericMethod(typeArguments);
      }

    /// <summary>
    /// Invokes the specified method.
    /// </summary>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    /// <param name="method">The method.</param>
    /// <param name="target">The target.</param>
    /// <param name="parameters">The parameters.</param>
    /// <returns>The result of method invocation.</returns>
    public static TResult Invoke<TResult>(this MethodInfo method, object target, params object[] parameters)
      {
      Contract.Requires<ArgumentNullException>(method != null);
      Contract.Requires<ArgumentNullException>(target != null || method.IsStatic);
      Contract.Requires<ArgumentNullException>(method.GetParameters().Length == parameters.Length);

      return (TResult)method.Invoke(target, parameters);
      }

    #endregion
    }
  }